package com.rr.mymoney.jsf;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;

import org.apache.log4j.Logger;
import org.primefaces.event.NodeCollapseEvent;
import org.primefaces.event.NodeExpandEvent;
import org.primefaces.event.NodeSelectEvent;
import org.primefaces.event.NodeUnselectEvent;
import org.primefaces.model.DefaultTreeNode;
import org.primefaces.model.TreeNode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;

import com.rr.mymoney.bo.CategoryBo;
import com.rr.mymoney.model.Category;
import com.rr.mymoney.model.util.ModelFactory;

// Spring instantiates the object, so there is no use for JSF annotations
@Controller
@Scope("view")
public class CategoriesController extends AbstractController {
    private static final long serialVersionUID = -6630386943820393002L;

    private static transient Logger log = Logger.getLogger(CategoriesController.class);

    private transient CategoryBo categoryBo;
    private TreeNode root;
    private TreeNode selectedNode;
    private Category category = ModelFactory.newCategory();
    private Map<String, String> possibleParents;

    @Autowired
    public CategoriesController(CategoryBo categoryBo) {
	super();
	this.categoryBo = categoryBo;
	getRoot();
	populatePossibleParents();
    }

    public TreeNode getRoot() {
	if (root == null) {
	    updateRoot();
	}
	// log.debug("getRoot: " + root.getChildCount() + " - " +
	// root.getChildren());
	return root;
    }

    private void updateRoot() {
	List<Category> categories = categoryBo.getByUserId(getLoggedUser().getId(), true);

	root = new DefaultTreeNode("root", null);
	populateTree(root, categories);
	// log.debug("updated root: " + root.getChildCount() + " - " +
	// root.getChildren());
    }

    private void populateTree(TreeNode root, List<Category> categories) {
	if (categories != null) {
	    for (Category category : categories) {
		categoryBo.check(category);
		TreeNode node = new DefaultTreeNode(category, root);
		node.setExpanded(true);
		log.debug("Populate tree: " + category);
		populateTree(node, category.getSubCategories());
	    }
	}
    }

    public TreeNode getSelectedNode() {
	// log.debug(this);
	// log.debug("Get selected node: " + selectedNode);
	return selectedNode;
    }

    public void setSelectedNode(TreeNode selectedNode) {
	category = ModelFactory.newCategory();

	if (selectedNode != null) {
	    categoryBo.check((Category) selectedNode.getData());
	}
	Category selectedCategory = (Category) selectedNode.getData();
	if (selectedCategory.getParentCategory() == null) {
	    selectedCategory.setParentCategory(new Category());
	}
	if (selectedNode != null) {
	    categoryBo.check((Category) selectedNode.getData());
	}
	this.selectedNode = selectedNode;
	// log.debug("Set selected node: " + selectedNode);
    }

    public void deleteCategory() {
	log.debug("delete: " + selectedNode.getData());
	selectedNode.getChildren().clear();
	selectedNode.getParent().getChildren().remove(selectedNode);
	selectedNode.setParent(null);
	categoryBo.delete((Category) selectedNode.getData());
	selectedNode = null;
	updateRoot();
    }

    public void saveCategory() {
	log.debug("save: " + category.toString());
	category.setUser(getLoggedUser());
	categoryBo.saveOrUpdate(category);
	category.getSubCategories();
	updateRoot();
	category = ModelFactory.newCategory();
    }

    public void updateCategory() {
	log.debug("update: " + selectedNode.toString());
	categoryBo.check((Category) selectedNode.getData());
	categoryBo.saveOrUpdate((Category) selectedNode.getData());
	categoryBo.check((Category) selectedNode.getData());
	selectedNode = null;
	updateRoot();
    }

    public Category getCategory() {
	return category;
    }

    public void setCategory(Category category) {
	this.category = category;
    }

    public void onNodeExpand(NodeExpandEvent event) {
	FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_INFO, "Expanded", event.getTreeNode().toString());

	FacesContext.getCurrentInstance().addMessage(null, message);
    }

    public void onNodeCollapse(NodeCollapseEvent event) {
	FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_INFO, "Collapsed", event.getTreeNode().toString());

	FacesContext.getCurrentInstance().addMessage(null, message);
    }

    public void onNodeSelect(NodeSelectEvent event) {
	FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_INFO, "Selected", event.getTreeNode().toString());

	FacesContext.getCurrentInstance().addMessage(null, message);
	// log.debug("onNodeSelect: " + event.getTreeNode());
    }

    public void onNodeUnselect(NodeUnselectEvent event) {
	FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_INFO, "Unselected", event.getTreeNode().toString());

	FacesContext.getCurrentInstance().addMessage(null, message);
	// log.debug("onNodeUnselect: " + event.getTreeNode());
    }

    public Map<String, String> getPossibleParents() {
	return possibleParents;
    }

    public void populatePossibleParents() {
	possibleParents = new HashMap<String, String>();
	List<Category> categories = categoryBo.getByUserId(getLoggedUser().getId(), true);
	for (Category category : categories) {
	    possibleParents.put(category.getName(), category.getId().toString());
	}
    }

    public Category getCategory(String id) {
	return categoryBo.get(Long.valueOf(id));
    }
}
